import json

with open('output/can.json') as file:
    can_data = json.load(file)


for node in can_data["can_node_list"]:
    node_name = node["node_name"]
    cpp_code = f"#pragma once\n\n"
    cpp_code += f"#include <chrono>\n"
    cpp_code += f'#include "can_parser.h"\n'
    cpp_code += f'#include "proto2json.h"\n'
    cpp_code += f'#include "can_param.h"\n'
    cpp_code += f'#include "can2proto.h"\n'
    cpp_code += f'#include "log.h"\n\n'

    cpp_code += '''
namespace {

uint64_t GetTimeStamp() {
  auto time_now = std::chrono::system_clock::now();
  auto time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
      time_now.time_since_epoch());
  return static_cast<uint64_t>(time_ms.count());
}

}  // namespace    
'''
    for message in node["messages"]:
        message_name = message["message_name"]
        port_id = message_name.lower()
        cpp_code += f"struct {message_name}_Parser {{\n"
        signal_names = []
        for signal in message["signals"]:
            signal_names.append(signal["signal_name"])
        # member: SignalParser 
        for signal_name in signal_names:
            cpp_code += f"    SignalParser {signal_name};\n\n"
        # method: constructor
        cpp_code += f"    {message_name}_Parser() {{\n"
        for signal_name in signal_names:
            cpp_code += f'        {signal_name} = CanConfiguration::Instance().SignalParsers()["{signal_name}"];\n'
        cpp_code += f"    }}\n\n"
        # method: Parser2Struct
        cpp_code += f"    void Parser(uint8_t* payload){{\n"
        for signal_name in signal_names:
            cpp_code += f"        {signal_name}.Parser(payload);\n"
        cpp_code += f"    }}\n\n"
        # method: CanParser2CanStruct_{message_name}_MSG
        cpp_code += f"    void CanParser2CanStruct_{message_name}_MSG({message_name}_MSG& msg) {{\n"
        cpp_code += f"        double factor;\n"
        cpp_code += f"        double offset;\n"
        for signal_name in signal_names:
            cpp_code += f"        factor = {signal_name}.factor;\n"
            cpp_code += f"        offset = {signal_name}.offset;\n"
            cpp_code += f"        msg.{signal_name} = *reinterpret_cast<std::decay<decltype(msg.{signal_name})>::type*>(&{signal_name}.signal);\n"
            cpp_code += f"        msg.{signal_name} = msg.{signal_name} * factor + offset;\n"
        cpp_code += f"    }}\n\n"
        # method: Process
        cpp_code += f"    bool Process(hobot::dataflow::spNoResourceProc& proc, uint8_t* payload){{\n"
        cpp_code += f"        Parser(payload);\n"
        cpp_code += f"        {message_name}_MSG can_struct;\n"
        cpp_code += f"        CanParser2CanStruct_{message_name}_MSG(can_struct);\n"
        cpp_code += f"        auto msg = std::make_shared<zpilot::candata::{message_name}_MSGMsg>();\n"
        cpp_code += f"        Struct2Proto_{message_name}_MSG(can_struct, &msg->proto);\n"
        cpp_code += f"        Proto2Json::toJson(msg->proto);\n"    
            # proc send
        cpp_code += f'        if (proc->GetOutputPort("{port_id}") != nullptr) {{\n'
        cpp_code += f'            DFHLOG_I("{port_id}");\n'
        cpp_code += f"            const auto time_update = GetTimeStamp();\n"
        cpp_code += f"            msg->SetGenTimestamp(time_update);\n"
        cpp_code += f"            msg->SetDoneTimestamp(time_update);\n"
        cpp_code += f'            return proc->GetOutputPort("{port_id}")->Send(msg) == 0;\n'
        cpp_code += f"         }}\n"
        cpp_code += f"         else {{\n"
        cpp_code += f'            DFHLOG_E("no such port {port_id}");\n'
        cpp_code += f'            return false;\n'
        cpp_code += f"         }}\n"
        # cpp_code += f"      }}\n\n"
        cpp_code += f"    }}\n"

        cpp_code += f"}};\n\n"


    with open(f'output/{node_name}_can_parser.h', 'w') as file:
        file.write(cpp_code)